From 19640a86280a407bbb865c291c1e79f514625fa1 Mon Sep 17 00:00:00 2001 From: oliskoli Date: Tue, 1 Apr 2008 18:50:50 +0000 Subject: [PATCH] Add reader for "OpenStreetMap" data files (format osm). git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@3158 f51c46e8-681c-474f-0cfe-069cfd0219fb --- gpsbabel/Makefile.in | 5 +- gpsbabel/osm.c | 691 +++++++++++++++++++++++++++++++++++++++++++ gpsbabel/testo | 5 + gpsbabel/vecs.c | 7 + 4 files changed, 707 insertions(+), 1 deletion(-) create mode 100644 gpsbabel/osm.c diff --git a/gpsbabel/Makefile.in b/gpsbabel/Makefile.in index 452deef86..659b53c72 100644 --- a/gpsbabel/Makefile.in +++ b/gpsbabel/Makefile.in @@ -58,7 +58,7 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o pcx.o cetus.o copilot.o \ yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \ wbt-200.o stmsdf.o gtrnctr.o dmtlog.o raymarine.o alan.o vitovtt.o \ ggv_log.o g7towin.o garmin_gpi.o lmx.o random.o xol.o dg-100.o \ - navilink.o mtk_logger.o ik3d.o + navilink.o mtk_logger.o ik3d.o osm.o FMTS=@FMTS@ @@ -597,6 +597,9 @@ nmn4.o: nmn4.c defs.h config.h queue.h gbtypes.h zlib/zlib.h zlib/zconf.h \ gbfile.h cet.h cet_util.h inifile.h csv_util.h nukedata.o: nukedata.c defs.h config.h queue.h gbtypes.h zlib/zlib.h \ zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h filterdefs.h +osm.o: osm.c defs.h config.h queue.h gbtypes.h zlib/zlib.h \ + zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h xmlgeneric.h \ + avltree.h overlay.o: overlay.c defs.h config.h queue.h gbtypes.h zlib/zlib.h \ zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h grtcirc.h ozi.o: ozi.c defs.h config.h queue.h gbtypes.h zlib/zlib.h zlib/zconf.h \ diff --git a/gpsbabel/osm.c b/gpsbabel/osm.c new file mode 100644 index 000000000..cc717b5a3 --- /dev/null +++ b/gpsbabel/osm.c @@ -0,0 +1,691 @@ +/* + + Reader for "OpenStreetMap" data files (.xml) + + Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA + +*/ + +#include "defs.h" +#include "avltree.h" +#include "xmlgeneric.h" + +static arglist_t osm_args[] = +{ + ARG_TERMINATOR +}; + +#define MYNAME "osm" + +#if ! HAVE_LIBEXPAT +void +osm_rd_init(const char *fname) +{ + fatal(MYNAME ": This build excluded \" MYNAME \" support because expat was not installed.\n"); +} + +void +osm_read(void) +{ +} + +#else + +static waypoint *wpt; +static route_head *rte; +static int wpt_loaded, rte_loaded; + +static avltree_t *waypoints; /* AVL tree */ +static avltree_t *keys = NULL; +static avltree_t *values = NULL; + +static xg_callback osm_node, osm_node_tag, osm_node_end; +static xg_callback osm_way, osm_way_nd, osm_way_tag, osm_way_end; + +static +xg_tag_mapping osm_map[] = { + { osm_node, cb_start, "/osm/node" }, + { osm_node_tag, cb_start, "/osm/node/tag" }, + { osm_node_end, cb_end, "/osm/node" }, + { osm_way, cb_start, "/osm/way" }, + { osm_way_nd, cb_start, "/osm/way/nd" }, + { osm_way_tag, cb_start, "/osm/way/tag" }, + { osm_way_end, cb_end, "/osm/way" }, + { NULL, 0, NULL } +}; + +static char *osm_features[] = { + "- dummy -", /* 0 */ + "aeroway", /* 1 */ + "amenity", /* 2 */ + "building", /* 3 */ + "cycleway", /* 4 */ + "railway", /* 5 */ + "highway", /* 6 */ + "historic", /* 7 */ + "landuse", /* 8 */ + "leisure", /* 9 */ + "man_made", /* 10 */ + "military", /* 11 */ + "natural", /* 12 */ + "place", /* 13 */ + "power", /* 14 */ + "shop", /* 15 */ + "sport", /* 16 */ + "tourism", /* 17 */ + "waterway", /* 18 */ + "aerialway", /* 19 */ + NULL +}; + +typedef struct osm_icon_mapping_s { + const char key; + const char *value; + const char *icon; +} osm_icon_mapping_t; + + +/* based on */ + +static osm_icon_mapping_t osm_icon_mappings[] = { + + /* cycleway ...*/ + + /* highway ...*/ + +// { 6, "mini_roundabout", "?" }, +// { 6, "stop", "?" }, +// { 6, "traffic_signals", "?" }, +// { 6, "crossing", "?" }, +// { 6, "gate", "?" }, +// { 6, "stile", "?" }, +// { 6, "cattle_grid", "?" }, +// { 6, "toll_booth", "?" }, +// { 6, "incline", "?" }, +// { 6, "incline_steep", "?" }, +// { 6, "viaduct", "?" }, +// { 6, "motorway_junction", "?" }, +// { 6, "services", "?" }, +// { 6, "ford", "?" }, +// { 6, "bus_stop", "?" }, +// { 6, "turning_circle", "?" }, +// { 6, "User Defined", "?" }, + + /* waterway ... */ + + { 18, "dock", "Dock" }, +// { 18, "lock_gate", "?" }, +// { 18, "turning_point", "?" }, +// { 18, "aqueduct", "?" }, +// { 18, "boatyard", "?" }, +// { 18, "water_point", "?" }, +// { 18, "waste_disposal", "?" }, +// { 18, "mooring", "?" }, +// { 18, "weir", "?" }, +// { 18, "User Defined", "?" }, + + /* railway ... */ + +// { 5, "station", "?" }, +// { 5, "halt", "?" }, +// { 5, "tram_stop", "?" }, +// { 5, "viaduct", "?" }, + { 5, "crossing", "Crossing" }, +// { 5, "level_crossing", "?" }, +// { 5, "subway_entrance", "?" }, +// { 5, "turntable", "?" }, +// { 5, "User Defined", "?" }, + + /* aeroway ... */ + + { 1, "aerodrome", "Airport" }, + { 1, "terminal", "Airport" }, + { 1, "helipad", "Heliport" }, +// { 1, "User Defined", "?" }, + + /* aerialway ... */ + +// { 19, "User Defined", "?" }, + + /* power ... */ + +// { 14, "tower", "?" }, +// { 14, "sub_station", "?" }, +// { 14, "generator", "?" }, + + /* man_made ... */ + +// { 10, "works", "?" }, +// { 10, "beacon", "?" }, +// { 10, "survey_point", "?" }, +// { 10, "power_wind", "?" }, +// { 10, "power_hydro", "?" }, +// { 10, "power_fossil", "?" }, +// { 10, "power_nuclear", "?" }, +// { 10, "tower", "?" }, +// { 10, "water_tower", "?" }, +// { 10, "gasometer", "?" }, +// { 10, "reservoir_covered", "?" }, +// { 10, "lighthouse", "?" }, +// { 10, "windmill", "?" }, +// { 10, "wastewater_plant", "?" }, +// { 10, "crane", "?" }, +// { 10, "User Defined", "?" }, + + /* building ... */ + + { 3, "yes", "Building" }, +// { 3, "User Defined", "?" }, + + /* leisure ... */ + +// { 9, "sports_centre", "?" }, + { 9, "golf_course", "Golf Course" }, + { 9, "stadium", "Stadium" }, +// { 9, "track", "?" }, +// { 9, "pitch", "?" }, +// { 9, "water_park", "?" }, + { 9, "marina", "Marina" }, +// { 9, "slipway", "?" }, + { 9, "fishing", "Fishing Area" }, +// { 9, "nature_reserve", "?" }, + { 9, "park", "Park" }, +// { 9, "playground", "?" }, +// { 9, "garden", "?" }, +// { 9, "common", "?" }, +// { 9, "User Defined", "?" }, + + /* amenity ... */ + + { 2, "pub", "Bar" }, +// { 2, "biergarten", "?" }, + { 2, "nightclub", "Bar" }, +// { 2, "cafe", "?" }, + { 2, "restaurant", "Restaurant" }, + { 2, "fast_food", "Fast Food" }, + { 2, "parking", "Parking Area" }, +// { 2, "bicycle_parking", "?" }, +// { 2, "bicycle_rental", "?" }, + { 2, "car_rental", "Car Rental" }, +// { 2, "car_sharing", "?" }, +// { 2, "taxi", "?" }, + { 2, "fuel", "Gas Station" }, + { 2, "telephone", "Telephone" }, + { 2, "toilets", "Restroom" }, +// { 2, "recycling", "?" }, +// { 2, "public_building", "?" }, + { 2, "townhall", "City Hall" }, +// { 2, "place_of_worship", "?" }, +// { 2, "grave_yard", "?" }, + { 2, "post_office", "Post Office" }, +// { 2, "post_box", "?" }, + { 2, "school", "School" }, +// { 2, "university", "?" }, +// { 2, "college", "?" }, + { 2, "pharmacy", "Pharmacy" }, + { 2, "hospital", "Medical Facility" }, +// { 2, "library", "?" }, + { 2, "police", "Police Station" }, +// { 2, "fire_station", "?" }, +// { 2, "bus_station", "?" }, +// { 2, "theatre", "?" }, +// { 2, "cinema", "?" }, +// { 2, "arts_centre", "?" }, +// { 2, "courthouse", "?" }, +// { 2, "prison", "?" }, + { 2, "bank", "Bank" }, +// { 2, "bureau_de_change", "?" }, +// { 2, "atm", "?" }, +// { 2, "fountain", "?" }, +// { 2, "User Defined", "?" }, + + /* shop ... */ + +// { 15, "supermarket", "?" }, + { 15, "convenience", "Convenience Store" }, +// { 15, "butcher", "?" }, +// { 15, "bicycle", "?" }, +// { 15, "doityourself", "?" }, +// { 15, "dry_cleaning", "?" }, +// { 15, "laundry", "?" }, +// { 15, "outdoor", "?" }, +// { 15, "kiosk", "?" }, +// { 15, "User Defined", "?" }, + + /* tourism ... */ + + { 17, "information", "Information" }, + { 17, "hotel", "Hotel" }, + { 17, "motel", "Lodging" }, + { 17, "guest_house", "Lodging" }, + { 17, "hostel", "Lodging" }, + { 17, "camp_site", "Campground" }, + { 17, "caravan_site", "RV Park" }, + { 17, "picnic_site", "Picnic Area" }, + { 17, "viewpoint", "Scenic Area" }, +// { 17, "theme_park", "?" }, +// { 17, "attraction", "?" }, + { 17, "zoo", "Zoo" }, +// { 17, "artwork", "?" }, + { 17, "museum", "Museum" }, +// { 17, "User Defined", "?" }, + + /* historic ... */ + +// { 7, "castle", "?" }, +// { 7, "monument", "?" }, +// { 7, "memorial", "?" }, +// { 7, "archaeological_site", "?" }, +// { 7, "ruins", "?" }, +// { 7, "battlefield", "?" }, +// { 7, "User Defined", "?" }, + + /* landuse ... */ + +// { 8, "farm", "?" }, +// { 8, "quarry", "?" }, +// { 8, "landfill", "?" }, +// { 8, "basin", "?" }, +// { 8, "reservoir", "?" }, + { 8, "forest", "Forest" }, +// { 8, "allotments", "?" }, +// { 8, "residential", "?" }, +// { 8, "retail", "?" }, +// { 8, "commercial", "?" }, +// { 8, "industrial", "?" }, +// { 8, "brownfield", "?" }, +// { 8, "greenfield", "?" }, +// { 8, "railway", "?" }, +// { 8, "construction", "?" }, + { 8, "military", "Military" }, + { 8, "cemetery", "Cemetery" }, +// { 8, "village_green", "?" }, +// { 8, "recreation_ground", "?" }, +// { 8, "User Defined", "?" }, + + /* military ... */ + +// { 11, "airfield", "?" }, +// { 11, "bunker", "?" }, +// { 11, "barracks", "?" }, +// { 11, "danger_area", "?" }, +// { 11, "range", "?" }, +// { 11, "naval_base", "?" }, +// { 11, "User Defined", "?" }, + + /* natural ... */ + +// { 12, "spring", "?" }, +// { 12, "peak", "?" }, +// { 12, "glacier", "?" }, +// { 12, "volcano", "?" }, +// { 12, "cliff", "?" }, +// { 12, "scree", "?" }, +// { 12, "scrub", "?" }, +// { 12, "fell", "?" }, +// { 12, "heath", "?" }, +// { 12, "wood", "?" }, +// { 12, "marsh", "?" }, +// { 12, "water", "?" }, +// { 12, "coastline", "?" }, +// { 12, "mud", "?" }, + { 12, "beach", "Beach" }, +// { 12, "bay", "?" }, +// { 12, "land", "?" }, +// { 12, "cave_entrance", "?" }, +// { 12, "User Defined", "?" }, + + /* sport ... */ + +// { 16, "10pin", "?" }, +// { 16, "athletics", "?" }, +// { 16, "australian_football", "?" }, +// { 16, "baseball", "?" }, +// { 16, "basketball", "?" }, +// { 16, "boules", "?" }, +// { 16, "bowls", "?" }, +// { 16, "climbing", "?" }, +// { 16, "cricket", "?" }, +// { 16, "cricket_nets", "?" }, +// { 16, "croquet", "?" }, +// { 16, "cycling", "?" }, +// { 16, "dog_racing", "?" }, +// { 16, "equestrian", "?" }, +// { 16, "football", "?" }, +// { 16, "golf", "?" }, +// { 16, "gymnastics", "?" }, +// { 16, "hockey", "?" }, +// { 16, "horse_racing", "?" }, +// { 16, "motor", "?" }, +// { 16, "multi", "?" }, +// { 16, "pelota", "?" }, +// { 16, "racquet", "?" }, +// { 16, "rugby", "?" }, +// { 16, "skating", "?" }, +// { 16, "skateboard", "?" }, +// { 16, "soccer", "?" }, + { 16, "swimming", "Swimming Area" }, + { 16, "skiing", "Skiing Area" }, +// { 16, "table_tennis", "?" }, +// { 16, "tennis", "?" }, +// { 16, "orienteering", "?" }, +// { 16, "User Defined", "?" }, + + /* place ... */ + +// { 13, "continent", "?" }, +// { 13, "country", "?" }, +// { 13, "state", "?" }, +// { 13, "region", "?" }, +// { 13, "county", "?" }, + { 13, "city", "City (Large)" }, + { 13, "town", "City (Medium)" }, + { 13, "village", "City (Small)" }, +// { 13, "hamlet", "?" }, +// { 13, "suburb", "?" }, +// { 13, "locality", "?" }, +// { 13, "island", "?" }, +// { 13, "User Defined", "?" }, + + { -1, NULL, NULL } +}; + + +static void +osm_features_init(void) +{ + int i; + + keys = avltree_init(0, MYNAME); + values = avltree_init(0, MYNAME); + + /* the first of osm_features is a place holder */ + for (i = 1; osm_features[i]; i++) + avltree_insert(keys, osm_features[i], (void *)i); + + for (i = 0; osm_icon_mappings[i].value; i++) { + char buff[128]; + + buff[0] = osm_icon_mappings[i].key; + strncpy(&buff[1], osm_icon_mappings[i].value, sizeof(buff) - 1); + avltree_insert(values, buff, (void *)&osm_icon_mappings[i]); + } +} + + +static char +osm_feature_ikey(const char *key) +{ + int result; + const void *data; + + if (avltree_find(keys, key, &data)) + result = (int)data; + else + result = -1; + + return result; +} + + +static char * +osm_feature_symbol(const char ikey, const char *value) +{ + char *result; + char buff[128]; + const void *data; + + buff[0] = ikey; + strncpy(&buff[1], value, sizeof(buff) - 1); + + if (avltree_find(values, buff, &data)) + result = xstrdup(((osm_icon_mapping_t *)data)->icon); + else + xasprintf(&result, "%s:%s", osm_features[(int)ikey], value); + + return result; +} + + +static char * +osm_strip_html(const char *str) +{ + utf_string utf; + utf.is_html = 1; + utf.utfstring = (char *)str; + + return strip_html(&utf); // util.c +} + + +static void +osm_node_end(const char *args, const char **unused) +{ + if (wpt) { + if (wpt->wpt_flags.fmt_use) + waypt_add(wpt); + else + waypt_free(wpt); + wpt = NULL; + } +} + + +static void +osm_node(const char *args, const char **attrv) +{ + const char **avp = &attrv[0]; + + wpt = waypt_new(); + + while (*avp) { + if (strcmp(avp[0], "id") == 0) { + xasprintf(&wpt->description, "osm-id %s", avp[1]); + if (! avltree_insert(waypoints, avp[1], (void *)wpt)) + warning(MYNAME ": Duplicate osm-id %s!\n", avp[1]); + else + wpt->wpt_flags.fmt_use = 1; + } + else if (strcmp(avp[0], "user") == 0) ; + else if (strcmp(avp[0], "lat") == 0) + wpt->latitude = atof(avp[1]); + else if (strcmp(avp[0], "lon") == 0) + wpt->longitude = atof(avp[1]); + else if (strcmp(avp[0], "timestamp") == 0) + wpt->creation_time = xml_parse_time(avp[1], &wpt->microseconds); + + avp += 2; + } +} + + +static void +osm_node_tag(const char *args, const char **attrv) +{ + const char **avp = &attrv[0]; + const char *key = "", *value = ""; + char *str; + char ikey; + + while (*avp) { + if (strcmp(avp[0], "k") == 0) + key = avp[1]; + else if (strcmp(avp[0], "v") == 0) + value = avp[1]; + avp+=2; + } + + str = osm_strip_html(value); + + if (strcmp(key, "name") == 0) { + if (! wpt->shortname) + wpt->shortname = xstrdup(str); + } + else if (strcmp(key, "name:en") == 0) { + if (wpt->shortname) + xfree(wpt->shortname); + wpt->shortname = xstrdup(str); + } + else if ((ikey = osm_feature_ikey(key)) >= 0) { + wpt->icon_descr = osm_feature_symbol(ikey, value); + wpt->wpt_flags.icon_descr_is_dynamic = 1; + } + else if (strcmp(key, "note") == 0) { + if (wpt->notes) { + char *tmp; + xasprintf(&tmp, "%s; %s", wpt->notes, str); + xfree(wpt->notes); + wpt->notes = tmp; + } + else + wpt->notes = xstrdup(str); + } + + xfree(str); +} + + +static void +osm_way(const char *args, const char **attrv) +{ + const char **avp = &attrv[0]; + + rte = route_head_alloc(); + + while (*avp) { + if (strcmp(avp[0], "id") == 0) { + xasprintf(&rte->rte_desc, "osm-id %s", avp[1]); + } + avp += 2; + } +} + +static void +osm_way_nd(const char *args, const char **attrv) +{ + const char **avp = &attrv[0]; + + while (*avp) { + if (strcmp(avp[0], "ref") == 0) { + waypoint *tmp; + if (avltree_find(waypoints, avp[1], (void *)&tmp)) { + tmp = waypt_dupe(tmp); + route_add_wpt(rte, tmp); + } + else + warning(MYNAME ": Way reference id \"%s\" wasn't listed under nodes!\n", avp[1]); + } + avp += 2; + } +} + +static void +osm_way_tag(const char *args, const char **attrv) +{ + const char **avp = &attrv[0]; + const char *key = "", *value = ""; + char *str; + + while (*avp) { + if (strcmp(avp[0], "k") == 0) + key = avp[1]; + else if (strcmp(avp[0], "v") == 0) + value = avp[1]; + avp += 2; + } + + str = osm_strip_html(value); + + if (strcmp(key, "name") == 0) { + if (! rte->rte_name) + rte->rte_name = xstrdup(str); + } + else if (strcmp(key, "name:en") == 0) { + if (rte->rte_name) + xfree(rte->rte_name); + rte->rte_name = xstrdup(str); + } + + xfree(str); +} + +static void +osm_way_end(const char *args, const char **unused) +{ + if (rte) { + route_add_head(rte); + rte = NULL; + } +} + +static void +osm_rd_init(const char *fname) +{ + wpt = NULL; + rte = NULL; + wpt_loaded = 0; + rte_loaded = 0; + + waypoints = avltree_init(0, MYNAME); + if (! keys) + osm_features_init(); + + xml_init(fname, osm_map, NULL); +} + +static void +osm_read(void) +{ + xml_read(); + avltree_done(waypoints); +} + +#endif + +static void +osm_rd_deinit(void) +{ + xml_deinit(); +} + +static void +osm_exit(void) +{ + if (keys) + avltree_done(keys); + if (values) + avltree_done(values); +} + + +ff_vecs_t osm_vecs = { + ff_type_file, + { + ff_cap_read, /* waypoints */ + ff_cap_none, /* tracks */ + ff_cap_read /* routes */ + }, + osm_rd_init, + NULL, + osm_rd_deinit, + NULL, + osm_read, + NULL, + osm_exit, + osm_args, + CET_CHARSET_UTF8, 0 +}; diff --git a/gpsbabel/testo b/gpsbabel/testo index 75999178d..dcb4d30ae 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -1303,5 +1303,10 @@ compare ${TMPDIR}/mtk_logger.csv ${REFERENCE}/track/mtk_logger.csv ${PNAME} -i ik3d -f ${REFERENCE}/ik3d-sample.ikt -o gpx -F ${TMPDIR}/ik3d-sample.gpx compare ${TMPDIR}/ik3d-sample.gpx ${REFERENCE}/ik3d-sample.gpx +# osm data files +rm -f ${TMPDIR}/osm-* +${PNAME} -i osm -f ${REFERENCE}/osm-data.xml -o gpx -F ${TMPDIR}/osm-data.gpx +compare ${TMPDIR}/osm-data.gpx ${REFERENCE}/osm-data.gpx + exit 0 diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index 3b90a1d25..e3169f184 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -131,6 +131,7 @@ extern ff_vecs_t random_vecs; extern ff_vecs_t xol_vecs; extern ff_vecs_t navilink_vecs; extern ff_vecs_t ik3d_vecs; +extern ff_vecs_t osm_vecs; static vecs_t vec_list[] = { @@ -732,6 +733,12 @@ vecs_t vec_list[] = { "MagicMaps IK3D project file (.ikt)", "ikt" }, + { + &osm_vecs, + "osm", + "OpenStreetMap data files", + "xml" + }, #endif // MAXIMAL_ENABLED { NULL, -- 2.30.2